﻿using FlyIL.Advance;
using FlyIL.Expression;
using FlyIL.Misc;
using FlyIL.Trace;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace FlyIL.Optimizer;



class MovPass : SimplfyRoot
{

    [DebuggerNonUserCode()]
    public override bool IsMatch(Context con, Inst node, ExpByTrace expression = null)
        => (node.Opt == Opt.mov || node.Opt == Opt.movzx || node.Opt == Opt.movsx);

    public virtual void DoSimply(Context con, Inst node, ExpByTrace exp = null)
    {
        return;
    }


    public override void Do(Context con, Inst node, ExpByTrace expression = null)
    {
        /// 查找 子处理 
        var a = GetSubMatch(con, node);
        if (a != null)
        {
            (a as MovPass).DoSimply(con, node);
        }
    }
}


internal class MovStackPassWithPush : MovPass
{
   


    public override bool IsMatch(Context con, Inst node, ExpByTrace exp = null)
        => (node.O1.IsStackValue &&  con.FindThePush(node, ref node.O1) != null)
            ;

    /// <summary>
    /// mov 到栈， 尝试翻译为  push xxx 
    /// </summary>
    public override void DoSimply(Context con, Inst node, ExpByTrace exp = null)
    {
        Inst pnode = con.FindThePush(node, ref node.O1); 

        if( pnode is null )
        {
            throw new System.Exception("不可能");
        }


        var inst = node;

        /// 针对当前节点， 查找 第一操作数
        /// 试想 如果是 mov a, 0x1234 , 则针对 第二操作数，追踪无意义
        Operand _ = inst.O1;
        exp ??= con.TraceStrict(node, ref _);

        /***********************
         * exp 有效， 且不是自身语句的 Op1 
         * 将 mov 栈 ， 化简为 push xxx 
         *是一个 直接操作数 (表达式 一层)
         * xxx 在两个节点间 不能有 修改 
         * 且原来的 push AAA , aaa 不能有传递 
         * 
         * 操作数 大于1 个 ， 也放弃
        ********************/

        if (exp == null || !exp.IsValid || exp.Collect<Operand>().Count > 1)
            exp = new ExpByTrace(inst.O2);


        if (exp is null || exp.CalcDepth > 0)
            return;

        bool canfix = true;

        /// 注意 , 这里是从 push 节点 , 开始检查  , 所以不能使用 追踪的表达式的 结果  
        /// 如果表达式， 是一个常量，则不需要检测 
        if (!exp.Calculable)
        {
            for (var it = node; it != null && it != pnode; it = con.GetPreviousIfExist(it))
            {
                var _1 = exp.FinalLeft<Operand>();
                if (it.WillAlter(_1))
                {
                    canfix = false;
                    break;
                }
            }
        }
        if (canfix && !con.HasUse(begin: con.GetNextIfExist(pnode), end: node, ref _))
        {
            con.Nop_And_Fix_A_StackAccess(node, (int)inst.O1.u32, inst.O1.ByteSize);
            if (exp.Calculable)
            {
                ulong val = exp.Result;
                var nnn = Operand.Make(val, inst.O1.ByteSize);
                pnode.push(nnn);
            }
            else
            {
                pnode.push(exp.FinalLeft<Operand>());
            }

            inst.nop();
        }
        else     /// 这里没有办法 化简为 push xxxx 
        {

            if (exp.Calculable)
            {
                ulong val = exp.Result;
                var nnn = Operand.Make(val, inst.O1.ByteSize);
                node.mov(inst.O1, nnn);
            }
            else
            {
                node.mov(inst.O1, exp.FinalLeft<Operand>());
            }

        }

        return;


    }

}

internal class MovStackPassWithoutPush : MovPass
{

    public override bool IsMatch(Context con, Inst node, ExpByTrace exp = null)
       => (node.O1.IsStackValue && con.FindThePush(node, ref node.O1) == null)
           ;

    /// <summary>
    /// mov 到栈， 但没有匹配到 push 
    /// </summary>
    public override void DoSimply(Context con, Inst node, ExpByTrace exp = null)
    {

        var inst = node;

        /// 针对当前节点， 查找 第一操作数
        /// 试想 如果是 mov a, 0x1234 , 则针对 第二操作数，追踪无意义
        Operand _ = inst.O1;
        exp ??= con.TraceWeak(node, ref _);

        /// exp 有效， 且不是自身语句的 Op1 
        /// 将 mov 栈 ， 化简为 push xxx 
        /// *是一个 直接操作数 (表达式 一层)
        /// * xxx 在两个节点间 不能有 修改 
        /// * 且原来的 push AAA , aaa 不能有传递 

        if (exp == null || exp.CalcDepth > 0)
            return;


        /// 那么有可能是错位的压栈 
        var xpushs = con.FindPushs(node, ref inst.O1);
        /// 目前只支持 两个压栈指令的情况 
        if (xpushs == null || xpushs.Count() != 2)
        {
            if (exp.IsValid)
            {
                if (exp.Calculable)
                    node.mov(inst.O1, exp.Result);
                else if (exp.CalcDepth == 0)
                    node.mov(inst.O1, exp.FinalLeft<Operand>());

            }

            return;
        }

        /// 目前只支持 后一个为 push 的情况 ， 
        /// 因为前一个 push 的话， 无法取 高位值（无对应的寄存器来处理)
        if (xpushs[1].node.Opt == Opt.push && xpushs[1].offset < 0 && xpushs[1].node.SpVariety + node.O2.ByteSize == 0)
            return;


        if (xpushs[0].node.Opt == Opt.lea)
        {

            var first = xpushs[0].offset;
            if (first == 0)
                xpushs[0].node.push(node.O2);
            else
            {
                xpushs[0].node.lea(con.RegSpExp, con.RegSpExp - first);
                con.InsertBefore(xpushs[0].node).push(node.O2);
            }
        }
        else if (xpushs[0].node.Opt == Opt.push)
        {
            var first = xpushs[0].offset;
            if (first == 0)
                xpushs[0].node.push(node.O2);
            else
            {

                var so = new Operand(xpushs[0].node.O1.Base.ChangTo((int)first));
                xpushs[0].node.push(so);
                con.InsertBefore(xpushs[0].node).push(node.O2);
            }
        }

        /// 正确的做法是在 第二个 压栈之前 ，添加要替换的压栈 
        if (xpushs[1].node.Opt == Opt.push)
        {
            var second = +node.O1.PtrSize + xpushs[1].offset + xpushs[1].node.SpVariety;
            if (second > node.O1.PtrSize)
            {
                if (xpushs[1].node.O1.IsRegister)
                    xpushs[1].node.push(new Operand(xpushs[1].node.O1.Base.ChangTo((int)second)));
                else if (xpushs[1].node.O1.IsPtr)
                {
                    var op1 = xpushs[1].node.O1;
                    op1.PtrSize -= 2;
                    op1.u32 += 2;
                    xpushs[1].node.O1 = op1;
                }
            }
            else
            {
                xpushs[1].node.nop();
            }
        }
        else if (xpushs[1].node.Opt == Opt.lea)
        {
            var second = +node.O1.PtrSize + xpushs[1].offset + xpushs[1].node.SpVariety;
            if (second < 0)
                xpushs[1].node.lea(con.RegSp, con.RegSpExp + second);
            else
                xpushs[1].node.nop();
        }

        node.nop();

        return;


    }

}


internal class MovNormalPass : MovPass
{

    public override bool IsMatch(Context con, Inst node, ExpByTrace exp = null)
       => (!node.O1.IsStackValue);

    /// <summary>
    /// 普通 mov
    /// </summary>
    /// <param name="remove_dead"> 是否移除 死代码</param>
    /// <param name="exp">处理Xchg时，需要提节追踪出表达式，防止因Pass处理，导致后面的追踪失败</param>
    /// <returns> mov 节点 是否需要 nop 掉 </returns>
    public override void DoSimply(Context con, Inst node, ExpByTrace exp = null)
    {
        var inst = node;


        if (inst.O1 == inst.O2)   /// 自已传给自己 
        {
            node.nop();
            return;
        }

        if (inst.O2.IsImm)   /// 自已传给自己 
        {
            var pre = con.RemoveDeadCode(node, ref inst.O1);
            con.SimplifyBetween(pre, node);
            return;
        }

        /// 赋值给寄存器 

        //!!! 此处是追踪第二操作数，需要从前一条语句开始， 防止因为干扰出错 
        exp ??= con.TraceStrict(node, ref inst.O1);


        if (exp == null || !exp.IsValid)
        {
            var pre = con.RemoveDeadCode(node, ref inst.O1);
            con.SimplifyBetween(pre, node);
            return;
        }

        if (exp.Calculable)
        {
            node.mov(inst.O1, Operand.Make(exp.Result, inst.O2.ByteSize));
        }
        else if (exp.CalcDepth == 0)
        {

            if (exp.Ext != null && exp.Ext.Opt == Opt.pop && !exp.HasFactorBeenChanged)
            {
                exp.Ext.pop(inst.O1);
                node.nop();
            }
            else
            {


                /// 找了个寂寞 
                if (exp.FinalLeft<Operand>() == inst.O2)
                {
                    /// 如果是 mov eax, [eax] 则不能 remove_dead 
                    if (inst.O2.Used(inst.O1))
                        ; /// 如果两个 操作数 相交， 则不能  RemoveDeadCode

                }
                else
                {
                    var found = exp.FinalLeft<Operand>();

                    if (found == inst.O1)
                    {
                        node.nop();
                        var pre = con.RemoveDeadCode(node, ref inst.O1);
                        con.SimplifyBetween(pre, node);
                        //remove_dead = true;

                    }
                    else if (found.Used(inst.O1))
                    {
                        /// 如果两个 操作数 相交， 则不能  RemoveDeadCode
                        ;// remove_dead = false;
                    }


                    if (found == inst.O1)
                        node.nop();
                    else
                        /// 可能是 movzx movsx 等这些 
                        node.make(inst.Opt, inst.O1, found);
                }
            }
        }
        else
        {
            /*********
             * 
             * 如果判断 表达式中 ， 有push , 有pop ,那么则需要将 将栈操作化去 
             * 因为 是基于栈的操作数 ， 故追踪的操作数 没有被修改 
             * 
             * ********/
            var nodes = exp.CollectDesc(exp.Depth);

            /// 各节点中 有出入栈 ， 且保持了 堆栈平衡 
            if (nodes.Any(o => o.SpVariety != 0) && nodes.Select(o => (int)o.SpVariety).Sum() == 0)
            {
                /// 因为这里是 一个mov , 所以 这里exp 需要改造 
                ;

                var simp = exp.Copy();

                simp.Simplify();

                var op = exp.FinalLeft<Operand>();

                simp.Replace(op, new Expr(node.O1, Opt.mov, op));

                con.Compile(node, exp, simp);

            }

        }

        return;

    }

}


